/* 
 * File:   vector3d.cpp
 * Author: chaterolas
 * 
 * Created on 18 de junio de 2009, 11:10 PM
 */

#include "vector3d.h"
#include <math.h>

/*
 * Crea una instancia de vector apartir de sus corrdenadas
 * Si no se especifican entonces se inicializan con el vector nulo
 * @param entrada x
 * @param entrada y
 * @param entrada z
 */
Vector3D::Vector3D(double x, double y, double z) {
    coor = new double[3];
    coor[0] = x;
    coor[1] = y;
    coor[2] = z;
}

/**
 * Genera una instancia de vector con las mismas caracteristicas que orig.
 * @param orig (el vector que se quiere copiar)
 */
Vector3D::Vector3D(const Vector3D& orig) {
    coor = new double[3];
    coor[0] = orig.coor[0];
    coor[1] = orig.coor[1];
    coor[2] = orig.coor[2];
}

/**
 * Destrulle la instancia
 */
Vector3D::~Vector3D() {
    delete [] coor;
}

/**
 * Regresa la coordenada especificada del vector, donde ese asocia
 * de este forma : x - 0, y - 1, z - 2
 * @param ent (entrada especidficada del vector)
 * @return coordenada especificada
 */
double Vector3D::getCoor(int ent) {
    return this->coor[ent];
}

/**
 * Asigna el valor val a la coordenada especificada del vector, donde ese asocia
 * de este forma : x - 0, y - 1, z - 2
 * @param ent (x, y, o z)
 * @param val (nuevo valor de la entrada)
 */
void Vector3D::setCoor(int ent, double val) {
    this->coor[ent] = val;
}

/**
 * Calcula el vector resultante de la suma de la instancia y algun otro.
 * @param v (el otro vector)
 * @return el resultado de la suma
 */
Vector3D *Vector3D::operator+(const Vector3D& v) {
    Vector3D *temp = new Vector3D();

    for(int i = 0; i < 3; i++)
          temp->coor[i] = coor[i] + v.coor[i];

    return temp;
}

/**
 * Agrega entrada a entrada los componentes de algun vector a este
 * @param v (Vector)
 */
void Vector3D::operator+=(const Vector3D& v) {

    for(int i = 0; i < 3; i++)
          coor[i] += v.coor[i];
}

/**
 * Calcula el vector resultante de que va del vector v al vector de instancia.
 * @parma v (el otro vector)
 * @return vector (el vector cuya direccion va de V a este)
 */
Vector3D *Vector3D::operator-(const Vector3D& v) {
    Vector3D *temp = new Vector3D();

    for(int i = 0; i < 3; i++)
          temp->coor[i] = coor[i] - v.coor[i];

    return temp;
}

/**
 * Disminuye entrada a entrada los componentes de algun vector a este
 * @param v (Vector)
 */
void Vector3D::operator-=(const Vector3D& v) {

    for(int i = 0; i < 3; i++)
          coor[i] -= v.coor[i];
}

/**
 * Multiplica po un escalar d este vector
 * @param d (escalar)
 * @return vector (el resultante de multiplicar cada entrada por d)
 */
Vector3D *Vector3D::operator *(double f) {
    return new Vector3D(coor[0]*f, coor[1]*f, coor[2]*f);
}

/**
 * Multiplica cada entrada de este vector por un escalar
 * @param f (escalar)
 */
void Vector3D::operator *=(double f) {
    for(int i = 0; i < 3; i++)
        coor[i] *= f;
}

/**
 * Calcula el vector ortogonal a la instancia y al vector v
 * con orientacion positiva.
 * @param v (el otro vector)
 * @return vector (producto cruz)
 */
Vector3D *Vector3D::cross(const Vector3D& v) {
    double x1 = coor[1]*v.coor[2] - coor[2]*v.coor[1];
    double x2 = coor[2]*v.coor[0] - coor[0]*v.coor[2];
    double x3 = coor[0]*v.coor[1] - coor[1]*v.coor[0];

    return new Vector3D(x1, x2, x3);
}

/**
 * Calcula el producto punto entre este vector y algun otro.
 * @param v (el otro vector)
 * @return producto punto
 */
double Vector3D::dot(const Vector3D& v) {
    return coor[0]*v.coor[0] + coor[1]*v.coor[1] + coor[2]*v.coor[2];
}

/**
 * Normaliza este vector.
 */
double Vector3D::getNorm() {
    return sqrt(this->dot(*this));
}

/**
 * Regresa este vector normalizado, es decir tal que su norma sea uno, y que
 * tenga la misma direccion y sentido
 * @return vector normalizado
 */
Vector3D *Vector3D::getNormalized() {
    double norma = getNorm();

    Vector3D *temp = new Vector3D(*this);

    if(norma != 0.0)
        *temp *= (double)(1.0 / norma);

    return temp;
}

void Vector3D::normalize() {
    double norma = getNorm();

    if(norma != 0.0)
        *this *= (double)(1.0 / norma);
}

/**
 * Decide si este y algun otro vector son ortogonales.
 * @param vector
 * @return 1 si son ortogonales, 0 en caso contrario
 */
int Vector3D::isNormalTo(const Vector3D& v) {
    double pp = this->dot(v);

    if(pp == 0)
        return  1;
    else
        return 0;
}

/**
 * Decide si este y otro vector son colineales
 * @param v (el otro vector)
 * @return 1 si son colineales, 0 en otro caso
 */
int Vector3D::isColinearTo(const Vector3D& v) {
    Vector3D *temp = cross(v);
    int val = 0;

    if(temp->coor[0] == 0.0 && temp->coor[1] == 0.0  && temp->coor[2] == 0.0) {
        val = 1;
    }

    delete temp;
    
    return val;
}
